<?php
use frontend\modules\yii2\widgets\layouts\markdown\Markdown;

$this->title = 'Yii模型（Model）数据校验器（rules）规则大全';
$this->registerMetaTag([
    'name' => 'keywords',
    'content' => 'yii,yii2,框架,应用结构,model，模型,模型类,使用,教程,rules,校验器'
]);
$this->registerMetaTag([
    'name' => 'description',
    'content' => 'Yii的模型类提供了非常丰富的校验器，可完成我们在开发中遇到的所有数据的校验需求。'
]);
$this->registerMetaTag([
    'name' => 'author',
    'content' => 'chenzhiwei'
]);


$html = [
    "数据校验" => <<<HTML
# Yii模型（Model）数据校验器（rules）规则大全
出于后端开发的专业性，我们不应该相信用户提交的一切信息，因此很有必要在处理数据前进行有效性验证。Yii提供了非常优秀的数据校验策略，你只需要在模型类中的`rules()`方法中定义数据的校验规则，就可以在前端和后端自动完成数据校验。

数据校验代码举例：
```php
namespace frontend\models;

use Yii;
use yii\base\Model;

class ContactForm extends Model
{
    public \$name;
    public \$email;
    public \$subject;
    public \$body;
    public \$verifyCode;

    public function rules()
    {
        return [
            // name, email, subject and body are required
            [['name', 'email', 'subject', 'body'], 'required'],

            // email has to be a valid email address
            ['email', 'email'],

            // verifyCode needs to be entered correctly
            ['verifyCode', 'captcha'],
        ];
    }

}
```

Yii提供了非常丰富的的数据校验器，如下所示：
```php
// vendor/yiisoft/yii2/validators/Validator.php
public static \$builtInValidators = [
    'boolean' => 'yii\validators\BooleanValidator',
    'captcha' => 'yii\captcha\CaptchaValidator',
    'compare' => 'yii\validators\CompareValidator',
    'date' => 'yii\validators\DateValidator',
    'datetime' => [
        'class' => 'yii\validators\DateValidator',
        'type' => DateValidator::TYPE_DATETIME,
    ],
    'time' => [
        'class' => 'yii\validators\DateValidator',
        'type' => DateValidator::TYPE_TIME,
    ],
    'default' => 'yii\validators\DefaultValueValidator',
    'double' => 'yii\validators\NumberValidator',
    'each' => 'yii\validators\EachValidator',
    'email' => 'yii\validators\EmailValidator',
    'exist' => 'yii\validators\ExistValidator',
    'file' => 'yii\validators\FileValidator',
    'filter' => 'yii\validators\FilterValidator',
    'image' => 'yii\validators\ImageValidator',
    'in' => 'yii\validators\RangeValidator',
    'integer' => [
        'class' => 'yii\validators\NumberValidator',
        'integerOnly' => true,
    ],
    'match' => 'yii\validators\RegularExpressionValidator',
    'number' => 'yii\validators\NumberValidator',
    'required' => 'yii\validators\RequiredValidator',
    'safe' => 'yii\validators\SafeValidator',
    'string' => 'yii\validators\StringValidator',
    'trim' => [
        'class' => 'yii\validators\FilterValidator',
        'filter' => 'trim',
        'skipOnArray' => true,
    ],
    'unique' => 'yii\validators\UniqueValidator',
    'url' => 'yii\validators\UrlValidator',
    'ip' => 'yii\validators\IpValidator',
];
```

我们在实际业务中会遇到各种清下的数据校验，希望下面的数据校验分类和举例能够帮到您。

## 自定义消息
虽然Yii有默认的校验失败后的消息返回，但不一定是我们想要的，这种情况下我们可以用`"message"=>""`自定义返回消息：
```php
public function rules()
{
    return [
        // name, email, subject and body are required
        [['name', 'email', 'subject', 'body'], 'required', "message" => "不能为空!"],

        // email has to be a valid email address
        ['email', 'email', "message" => "邮箱错误！"],

        // verifyCode needs to be entered correctly
        ['verifyCode', 'captcha', "message" => "验证码错误!"],
    ];
}
```

## 必须要填（required）
设置了`required`属性，就表示您必须要完成属性的赋值，最常见的使用场景就是登录，您必须要输入账号、密码才能进行验证：
```php
public function rules()
{
    return [
        // username and password are both required
        [['username', 'password'], 'required'],
    ];
}
```
## URL校验
默认的情况下您所输入的`url`必须要以`http`或者`https`开头，否则无法通过`url`规则：
```php
// 可以完成http、https的url校验，如果没有http或者https开头，则会报错
// 校验成功：http://yii.lvzmen.com
// 校验失败：yii.lvzmen.com，如果想要这种情况下通过校验，需要使用`defaultScheme`
['website', 'url'] 

// 只允许http协议的URL，屏蔽https的URL
// 校验成功：http://yii.lvzmen.com
// 校验失败：https://yii.lvzmen.com
["website", "url", 'validSchemes' => ['http']] 
```
如果你的`url`不确定是否有`http`前缀，那你就需要`defaultScheme`指定默认的前缀：
```php
// 如果您输入yii.lvzmen.com，则会自动补全：http://yii.lvzmen.com，然后进行进一步校验
// 校验成功：yii.lvzmen.com
["website", "url", 'defaultScheme' => 'http']
```
校验失败时默认的返回消息为：
```php
"message" => "Url is not a valid URL."
```
## 不校验（safe）
这种校验规则不会对字段的值进行校验：
```php
public function rules()
{
    return [
        // 屏蔽Yii模型对本字段的数据校验，不管`description`填什么都对
        ['description', 'safe'],
    ];
}
```
## Boolean校验
此校验器不校验`true`或`false`，而是校验`0`和`1`：
```php
["active", "boolean"],
```
默认情况下是弱类型校验，也就是说`0`和`1`是整型还是字符型都可以完成校验，如果你希望类型得到区分，可以使用`strict`：
```php
["active", "boolean", "strict" => true],
```
上述代码表示只有字符型的`0`和`1`才能通过校验器。
默认的校验失败输出为：
```php
"message" => 'Active must be either "1" or "0".'
```
HTML,
    "function" => <<<HTML
## 万能校验
虽然说Yii提供了丰富的过滤器，但是业务是千变万化的，有些数据校验非常复杂，用Yii提供的过滤器并不能实现我们的校验，此时我们可以定义校验函数：
```php
// 验证成功：age = 20
// 验证失败：age = 17 （未满18岁不能注册！）
["age", function(){
    if (\$this->age < 18) {
        \$this->addError("age", "未满18岁不能注册！");
    }
}]
```
HTML,
    "compare" => <<<HTML
## 比较器（compare）
比较器既可以完成特定的值比较也可以完成字段间的比较，我们分类讨论：

**值比较：**
```php
// age == 20
["age", "compare", "compareValue" => 20],
```
如果没有加`operator`，则默认是等值比较（==）;

**字段间比较：**  

比如说你的模型类中有个入学年纪（school_age），每个省的入学年纪都不同，因此`age`字段就需要和`school_age`进行比较：
```php
// age >= school_age
["age", "compare", "operator" => ">=", "compareAttribute" => "school_age"],
```
上述代码要求您输入的`age`必须要比`school_age`大，否则报错如下：
```php
"age" => "Age must be greater than or equal to 'School Age'."
``` 
当你同时设置了值比较和字段比较，那字段比较会失效，如下：
```php
// age只会和1比较，不会和school_age比较
["age", "compare", "operator" => ">=",  "compareValue" => 1, "compareAttribute" => "school_age"],
```
如果你既没有设置值比较也没有设置字段比较，那默认会进行字段间比较，比较的对象是当前属性加`_repeat`（`age_repeat`）后缀，如： 
```php
// age >= age_repeat，如果你的模型类中没有age_repeat字段，则会报错！
["age", "compare", "operator" => ">="],
```
可以完成如下列表的比较行为：

|Operator|Error Message|
| :---: | :---- |
| == |{attribute} must be equal to "{compareValueOrAttribute}".|
|=== |{attribute} must be equal to "{compareValueOrAttribute}".|
| != |{attribute} must not be equal to "{compareValueOrAttribute}".|
|!== |{attribute} must not be equal to "{compareValueOrAttribute}".|
|  > |{attribute} must be greater than "{compareValueOrAttribute}".|
| >= |{attribute} must be greater than or equal to "{compareValueOrAttribute}".|
| <  |{attribute} must be less than "{compareValueOrAttribute}".|
| <= |{attribute} must be less than or equal to "{compareValueOrAttribute}".|


HTML,
    "date" => <<<HTML
## 日期校验（date）
您可以对指定格式的日期进行有效性校验，日期校验器允许您校验下面三种类型的时间：
- date
- datatime
- time

举例如下：
```php
// created_at = "2021-12-5"
["created_at", "date", "format" => "php:Y-m-d"],

// created_at = "2021-12-05 13:18:11"
["created_at", "date", "type" => "datetime", "format" => "php:Y-m-d H:i:s"],
// 同等于
["created_at", "datetime", "format" => "php:Y-m-d H:i:s"],

// created_at = "13:18:11"
["created_at", "date", "type" => "time", "format" => "php:H:i:s"],
// 同等于
["created_at", "time", "format" => "php:H:i:s"],
```

日期验证器也支持范围校验：
```php
// created_at >= "2021-12-06" && created_at <= "2021-12-30"
["created_at", "date", "format" => "php:Y-m-d", "min" => "2021-12-06", "max" => "2021-12-30"],
```


HTML,
    "double" => <<<HTML
## 数字校验（double）
这个校验器可以校验整型和浮点型，也可以校验范围：
```php
// 校验成功：money = 99.9
["money", "double"],

// 范围校验： money >= 10 && money <= 50
["money", "double", "min" => 10, "max" => 50],

// 如果对边界有要求，可以用compare校验器： money > 10 && money <= 50
["money", "compare", "operator" => ">", "compareValue" => "10"]
["money", "compare", "operator" => "<=", "compareValue" => "50"]

// 整型校验
["money", "double", "integerOnly" => true, "min" => 10, "max" => 50],
// 与下面的同等效果
["money", "integer", "min" => 10, "max" => 50],

// 数组校验：对数组的每个元素进行校验，支持非严格模式
// 校验成功：money = 20
// 校验成功：money = [20, 30]
// 校验成功：money = [10, '20.1']
["money", "double", "allowArray" => true, "min" => 10, "max" => 50],

```
HTML,
    "email" => <<<HTML
## 邮件校验（email）
邮箱格式校验用法如下：
```php
// 验证成功： email = "chenzhiweiwk@lvzmen.com"
// 验证成功： email = "chenzhiweiwk@lvzmen.xyz"
["email", "email"]
```
邮箱域名有效性校验：
```php
// 验证成功： email = "chenzhiweiwk@lvzmen.com"
// 验证失败： email = "chenzhiweiwk@lvzmen.xyz"，因为lvzmen.xyz并不存在
["email", "email", "checkDNS" => true]
```

默认的校验失败返回文本为：
```php
"email" => "Email is not a valid email address."
```

HTML,
    "each" => <<<HTML
## 遍历校验（each）
对数组内的元素按照指定的校验器进行校验：
```php
// 校验成功：ages = [1,2,3,4,5]
// 校验失败：ages = [1,2,3,4,5.1] (Ages must be an integer.)
// 校验失败：ages = [1,2,3,4,5,300] (Ages must be no greater than 200.)
["ages", "each", "rule" => ["integer", "min" => 1, "max" => 200]]
```
`rule`仅仅支持单个校验器，如果需要支持多个校验器可以针对属性写多个`each`校验，如：
```php
// ages 既要满足是是整型数组，也要满足任意一个元素在1~200之间
["ages", "each", "rule" => ["integer"]]
["ages", "each", "rule" => ["double", "min" => 1, "max" => 200]]
```


HTML,
    "file" => <<<HTML
## 文件校验（file）
文件校验有如下内容的校验：
- 扩展名：png，jpeg，pdf...
- MIME types：text/plain，image/png
- 文件大小：minSize，maxSize
- 文件数量：minFiles，maxFiles

```php
// 文件必须是jpg/png/jpeg为扩展名且最大为5M
["upload", "file", "extensions" => "jpg,png,jpeg", "maxSize" => 5 * 1024 * 1024]
```

HTML,
    "image" => <<<HTML
## 图片校验（image）
用于文件校验是否是图片，并且可以限制图片的像素宽度和长度，注意：不对扩展名进行校验。
```php
// 只能上传最大宽度为1000px，最大高度为1000px的图片
["imageUpload", "image", "maxWidth" => 1000, "maxHeight" => 500]
```

HTML,
    "in" => <<<HTML
## 枚举校验（in）
用于校验数据是否在给定的一个集合中：
```php
// 验证成功：gendor = "male"
// 验证失败：gendor = "男"
["gendor", "in", "range" => ["male", "female"]]
```

我们可以用`not`来控制非操作，用于过滤不在指定集合的情况：
```php
// 输入的国家不能是"China", "Japan"
// 校验失败：country = "China"
// 校验成功：conntry = "US"
["country", "in", "not" => true, "range" => ["China", "Japan"]]
```

当`range`里是数字的时候，可以通过`strict`来控制是否进行类型校验：
```php
// 校验成功： active = 1
// 校验失败： active = "1"
["active", "in", "strict" => true, "range" => [0, 1]]
```

对数组进行遍历校验：
```php
// 校验成功： active = [0,1,0,1,1]
// 校验失败： active = [0,1,0,1,1,2]
["active", "in", "allowArray" => true, "range" => [0, 1]]
```

HTML,
    "filter" => <<<HTML
## 过滤器（filter）
这个过滤器并不是校验器，可以把它理解为数据的预处理，比如说：将输入的数组类型的ip列表转换为字符串。
```php
// 输入 ips = ["172.18.0.1", "172.18.0.2"] 输出："172.18.0.1,172.18.0.2"
["ips", "filter", "filter" => function(\$ip){
    return implode(",", \$ip);
}]
```
注意：要调用`\$model->validate()`才会实现预处理。

HTML,
    "match" => <<<HTML
## 正则校验（match）
通过编写正则表达式进行数据校验，使用方法如下：
```php
// 验证成功：tel = "18112312138"
// 验证失败：tel = "1811231213a"
['tel','match','pattern'=>'/^[\+ 0-9]*$/','message'=>'This phone number is invalid!'],
```
通过`not`可以取非操作：
```php
// 验证失败：tel = "18112312138"
// 验证成功：tel = "1811231213a"
['tel','match', "not" => true ,'pattern'=>'/^[\+ 0-9]*$/','message'=>'This phone number is invalid!'],
```

HTML,
    "ip" => <<<HTML
## IP校验
用于校验IP的有效性，也可以对特定的IP进行过滤（黑白名单处理），如果仅仅是校验IP的格式，可以使用如下代码：
```php
// 验证成功：127.0.0.1
// 验证失败：127.0.0.a
['ip_address', 'ip']
```
使用`ranges`设置白名单：
```php
['ip_address', 'ip', 'ranges' => ['192.168.0.0/24']]
```
Yii将一些常用的过滤配置（如：所有IP，本地IP，localhost等）封装起来用网络别名进行调用，以方便用户使用，常见的网络别名如下：
- `*`: any
- `any`: 0.0.0.0/0, ::/0
- `private`: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fd00::/88::/32
- `multicast`: 224.0.0.0/4, ff00::/8
- `linklocal`: 169.254.0.0/16, fe80::/10
- `localhost`: 127.0.0.0/8', ::1
- `documentation`: 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32
- `system`: multicast, linklocal, localhost, documentation

使用方法如下：
```php
// 接受任何IP，但是除了'192.168.0.0/24'子网的IP
['ip_address', 'ip', 'ranges' => [!'192.168.0.0/24', 'any']]
```


HTML,

];




echo Markdown::widget(["content" => implode("\n", $html)]);

